﻿using System;
using System.Collections;
using System.Collections.Generic;
using TPF.ObjectCache;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;
using XAsset;
using Random = UnityEngine.Random;

public class TestLoader : MonoBehaviour
{
    private const string ResPathBig = "Assets/ExpAssets/Boxes/Box_Big.prefab";
    private const string ResPath1 = "Assets/ExpAssets/Boxes/Box_1.prefab";
    private const string ResPath2 = "Assets/ExpAssets/Boxes/Box_2.prefab";
    private const string ResPath3 = "Assets/ExpAssets/Boxes/Box_3.prefab";
    private const string ResPath4 = "Assets/ExpAssets/Boxes/Box_4.prefab";
    private const string ResPath5 = "Assets/ExpAssets/Boxes/Box_5.prefab";
    private const string ResPath6 = "Assets/ExpAssets/Boxes/Box_6.prefab";


    // Use this for initialization
    private void Start()
    {
        XAsset.Assets.Initialize();

        StartCoroutine(Test());
    }

    // Update is called once per frame
    private void Update()
    {

    }

    private IEnumerator Test()
    {
        var objectPool = new GameObjectPool("TestPool");

        yield return new WaitForSeconds(0.5f);

        //yield return TestUIAssets();

        //yield return TestObjectPoolLoadSync(objectPool);
        yield return TestObjectPoolLoadAsync(objectPool);
        //yield return TestLifeTimeRule();
        //yield return TestAmountLimitRule();

        Debug.Log("Test Done");
    }

    // Test For XAsset ===========================================
    private IEnumerator TestUIAssets()
    {
        var cachedAssets = new List<GoAssetEntity>();

        var canvas = FindObjectOfType<Canvas>();
        Assert.IsNotNull(canvas);

        // 加载一个UI对象
        var winAsset = Assets.Load<GameObject>("Assets/ExpAssets/UI/WinWithAtlasSprite.prefab");
        Assert.IsNotNull(winAsset);
        var win = InstantiateAsset(cachedAssets, winAsset);
        win.transform.SetParent(canvas.transform, false);

        yield return new WaitForSeconds(1);

        // 从Sprite对象池中加载一个Sprite
        var spriteAsset = Assets.Load<Sprite>("Assets/ExpAssets/Sprite/Atlas2_2440006.png");
        Assert.IsNotNull(spriteAsset);
        win.transform.GetChild(0).GetComponent<Image>().sprite = (Sprite)spriteAsset.asset;

        yield return new WaitForSeconds(1);

        // 加载其他源格式的Sprite
        //var jpgSpriteAsset = Assets.Load<Sprite>("Assets/ExpAssets/Sprite/Atlas2_2440009_jpg.jpg");
        //Assert.IsNotNull(jpgSpriteAsset);
        //win.transform.GetChild(0).GetComponent<Image>().sprite = (Sprite)spriteAsset.asset;

        Destroy(win);
        winAsset.Release();
        spriteAsset.Release();
    }

    // Test For Object Pool ===========================================

    private IEnumerator TestObjectPoolLoadSync(GameObjectPool objectPool)
    {
        var go_big_1 = SetParentToNull(RandomSetGameObjectPosition(objectPool.Get(ResPathBig)));
        var go_big_2 = SetParentToNull(RandomSetGameObjectPosition(objectPool.Get(ResPathBig)));
        var go_1_1 = RandomSetGameObjectPosition(objectPool.Get(ResPath1));

        yield return new WaitForSeconds(2);

        objectPool.Recycle(go_big_1);
        yield return new WaitForSeconds(1);
        objectPool.Recycle(go_big_2);
        yield return new WaitForSeconds(1);

        RandomSetGameObjectPosition(objectPool.Get(ResPathBig));

        objectPool.Clear();

        Debug.Log("Test object pool load sync passed!");
    }

    private IEnumerator TestObjectPoolLoadAsync(GameObjectPool objectPool)
    {
#if UNITY_EDITOR
        if (XAsset.Utility.ActiveBundleMode)
        {
#endif
            // 测试Cancel接口
            var requestCancel = objectPool.GetAsync(ResPath6);
            requestCancel.Complete(a => Assert.IsTrue(false));
            requestCancel.Cancel(); // 由于加载完立即取消，所以无法收到完成时的回调
#if UNITY_EDITOR
        }
#endif

        // 测试正常异步加载
        GameObject go_5_1 = null;
        var request_go_5_1 = objectPool.GetAsync(ResPath5);
        request_go_5_1.Complete(a =>
        {
            go_5_1 = a;
            RandomSetGameObjectPosition(a);
        });
        yield return request_go_5_1;

        objectPool.Recycle (go_5_1);
        // 对象已回收
        Assert.IsTrue(!go_5_1.activeInHierarchy);

        // 测试同时加载多个对象
		yield return new WaitForSeconds (1);
        var go_list_1 = new List<GameObject>();
        var go_list_2 = new List<GameObject>();
		for (var i = 0; i < 10; i++) {
			objectPool.GetAsync (ResPath2).Complete (a => 
			{
				RandomSetGameObjectPosition(a);
                go_list_1.Add(a);
			});

			objectPool.GetAsync (ResPathBig).Complete (a => 
			{
				RandomSetGameObjectPosition(a);
                go_list_2.Add(a);
			});
		}

        yield return new WaitForSeconds(3); // 等待加载完成 

        Assert.AreEqual(go_list_1.Count, 10);
        Assert.AreEqual(go_list_2.Count, 10);

        for (var i = 0; i < 10; i++)
        {
            Assert.IsTrue(go_list_1[i] != null);
            Assert.IsTrue(go_list_2[i] != null);
        }

        for (var i = 0; i < 10; i++)
        {
            objectPool.Recycle(go_list_1[i]);
        }

        yield return new WaitForSeconds(0.5f);

        for (var i = 0; i < 10; i++)
        {
            Assert.IsTrue(!go_list_1[i].activeInHierarchy);
        }

        // 验证对象池中回收的对象数为10个
        Assert.IsTrue(objectPool.PoolCore.PooledObjects[ResPath2].Count == 10);

		yield return new WaitForSeconds (2);

        // 测试加载同一个key的多个资源，不会同一帧全部回调回来
#if UNITY_EDITOR
        if (XAsset.Utility.ActiveBundleMode)
        {
# endif
            var go_list_3 = new List<GameObject>();
            Action<GameObject> onGameObjectLoadDone = go =>
            {
                RandomSetGameObjectPosition(go);
                go_list_3.Add(go);
            };

            var go_1_request_1 = objectPool.GetAsync(ResPath1);
            go_1_request_1.Complete(onGameObjectLoadDone);
            var go_1_request_2 = objectPool.GetAsync(ResPath1);
            go_1_request_2.Complete(onGameObjectLoadDone);
            var go_1_request_3 = objectPool.GetAsync(ResPath1);
            go_1_request_3.Complete(onGameObjectLoadDone);
            var go_1_request_4 = objectPool.GetAsync(ResPath1);
            go_1_request_4.Complete(onGameObjectLoadDone);
            var go_1_request_5 = objectPool.GetAsync(ResPath1);
            go_1_request_5.Complete(onGameObjectLoadDone);
            var go_1_request_6 = objectPool.GetAsync(ResPath1);
            go_1_request_6.Complete(onGameObjectLoadDone);
            var go_1_request_7 = objectPool.GetAsync(ResPath1);
            go_1_request_7.Complete(onGameObjectLoadDone);
            var go_1_request_8 = objectPool.GetAsync(ResPath1);
            go_1_request_8.Complete(onGameObjectLoadDone);
            var go_1_request_9 = objectPool.GetAsync(ResPath1);
            go_1_request_9.Complete(onGameObjectLoadDone);

            yield return go_1_request_1;
            yield return new WaitForEndOfFrame();

            // 默认加载队列限制的回调为3，所以第一批回调完成时，go_list_3的数量应该是3
            Assert.AreEqual(go_list_3.Count, 3);

            // 第二批又增加了3个
            yield return new WaitForEndOfFrame();
            Assert.AreEqual(go_list_3.Count, 6);

            objectPool.Dump();

            // 将两个正在加载中的资源Cancel，最终go_list_3的数量应该是7
            go_1_request_7.Cancel();
            go_1_request_8.Cancel();

            yield return new WaitForEndOfFrame();
            Assert.AreEqual(go_list_3.Count, 7);

#if UNITY_EDITOR
        }
#endif

                objectPool.Clear ();

        Debug.Log("Test object pool load async passed!");
    }

    private IEnumerator TestLifeTimeRule()
    {
        using (var objectPool = new GameObjectPool("TestLifeTimeRulePool"))
        {
            // 使用LifeTime规则，检测间隔为1秒，对象生存时间为2秒
            objectPool.ApplyRule(new LiveTimeRule {CheckInternal = 1, ObjectLifeTime = 2});

            // 1.测试资源回收后超时
            // 加载ResPathBig
            var request_go_big_1 = objectPool.GetAsync(ResPathBig);
            yield return request_go_big_1;
            var go_big_1 = request_go_big_1.GameObject;
            RandomSetGameObjectPosition(go_big_1);

            // 一秒后回收
            yield return new WaitForSeconds(1);
            objectPool.Recycle(go_big_1);

            // 等待三秒测试规则是否生效
            yield return new WaitForSeconds(3.5f);
            // 此时ResPathBig应该已经被删除
            Assert.AreEqual(objectPool.PoolCore.PooledObjects[ResPathBig].Count, 0);
            // 模板已被删除
            Assert.IsTrue(!objectPool.PoolCore.TemplateAssets.ContainsKey(ResPathBig));

            // 2.测试资源没有被完全回收，超时仅删除池对象中的对象，不会删除模板
            // 加载ResPath2，创建两个对象
            var go_2_1 = objectPool.Get(ResPath2);
            RandomSetGameObjectPosition(go_2_1);
            var go_2_2 = objectPool.Get(ResPath2);
            RandomSetGameObjectPosition(go_2_2);

            // 一秒后回收一个对象
            yield return new WaitForSeconds(1f);
            objectPool.Recycle(go_2_1);

            // 等待三秒测试规则是否生效 
            yield return new WaitForSeconds(3.5f);
            // 激活的对象剩下一个
            Assert.AreEqual(objectPool.PoolCore.ActivatedObjects.Count, 1);
            // 此时池中的对象应该已经被删除
            Assert.AreEqual(objectPool.PoolCore.PooledObjects[ResPath2].Count, 0);
            // 模板还存在
            Assert.IsTrue(!objectPool.PoolCore.TemplateAssets.ContainsKey(ResPathBig));

            Debug.Log("Test life time rule passed!");
        }
    }

    private IEnumerator TestAmountLimitRule()
    {
        using (var objectPool = new GameObjectPool("TestAmountLimitRulePool"))
        {
            // 使用AmountLimitRule规则，最大数量为3
            objectPool.ApplyRule(new AmountLimitRule {MaxAmount = 3});

            // 1.测试回收对象池数量不到3时，规则不会清除池中的对象
            var list_1 = new List<GameObject>();
            // 创建5个对象，并回收2个
            for (var i = 0; i < 5; i++)
            {
                var tmp_go_4 = objectPool.Get(ResPath4);
                RandomSetGameObjectPosition(tmp_go_4);
                list_1.Add(tmp_go_4);
            }

            Assert.AreEqual(objectPool.PoolCore.ActivatedObjects.Count,  5);

            objectPool.Recycle(list_1[4]);
            objectPool.Recycle(list_1[3]);
            list_1.RemoveAt(4);
            list_1.RemoveAt(3);

            Assert.AreEqual(objectPool.PoolCore.ActivatedObjects.Count, 3);

            // 此时池中的对象数量应该为2
            Assert.AreEqual(objectPool.PoolCore.PooledObjects[ResPath4].Count, 2);

            // 2.测试回收数量超过3时，规则将清除多余的池中对象
            for (var i = 0; i < 3; i++)
            {
                objectPool.PoolCore.Recycle(list_1[i]);
            }

            list_1.Clear();
            Assert.AreEqual(objectPool.PoolCore.ActivatedObjects.Count, 0);

            // 此时池中的对象数量应该为3
            Assert.AreEqual(objectPool.PoolCore.PooledObjects[ResPath4].Count, 3);

            yield return new WaitForSeconds(1f);

            Debug.Log("Test amount limit rule passed!");
        }
    }


    private GameObject InstantiateAsset(List<GoAssetEntity> cachedAssets, Asset a)
    {
        if (a == null || a.asset == null)
        {
            return null;
        }

        var go = (GameObject) Instantiate(a.asset);

        RandomSetGameObjectPosition(go);

        cachedAssets.Add(new GoAssetEntity
        {
            Asset = a,
            GameObjects = new List<GameObject> {go}
        });

        return go;
    }

    private GameObject RandomSetGameObjectPosition(GameObject go)
    {
        go.transform.position = new Vector3(
            Random.Range(-2, 2),
            Random.Range(-3, 3),
            Random.Range(-3.5f, 3.5f));

        return go;
    }

    private GameObject SetParentToNull(GameObject go)
    {
        go.transform.parent = null;
        return go;
    }
}